package demo.isolib {
	import as3isolib.display.IsoSprite;
	import as3isolib.display.scene.IsoGrid;
	import as3isolib.geom.Pt;
	import as3isolib.utils.IsoDrawingUtil;
	
	import com.sshc625.utils.ArrayUtil;
	
	import demo.isolib.SpriteSheet;
	import com.sshc625.log.Logger;
	import demo.isolib.mousehandler.*;
	import demo.isolib.movement.BasicMove;
	import demo.isolib.movement.MoveMode;
	import demo.isolib.movement.TileMovement;
	import demo.util.DisplayObjectContainerUtil;
	
	import eDpLib.events.ProxyEvent;
	
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.events.MouseEvent;
	import flash.filters.GlowFilter;
	import flash.text.TextField;
	
	import org.casalib.util.ArrayUtil;

	/**
	 * 
	 */
	public class Tile extends IsoSprite {
		static public const DEFAULT_HEIGHT:Number = 0;

		public var selectable:Boolean = true;
		public var dragable:Boolean = true;
		
		public var movingMode:MoveMode = MoveMode.CALL;	// CALL by default.
		
		protected var _walkable:Boolean;
		protected var _movable:Boolean;		
		protected var _cellSize:Number = 1;
		protected var _colSpan:Number; // 
		protected var _rowSpan:Number;
		protected var _grid:IsoGrid;
		protected var _world:World;

		protected var _selected:Boolean;	// active or not.
		protected var _mouseDown:Boolean;
		protected var _column:Number; // 
		protected var _row:Number;
		protected var _moveBehavior:TileMovement;

		protected var _name:String = "Tile";
		protected var _isIdle:Boolean;
		protected var _skins:Array;
		protected var _faceDirection:String;
		protected var _baseSprite:Sprite;// the base of tile.
		
		protected var _mouseClickHandler:IMouseHandler; // 

		protected var _mouseOverHandler:IMouseHandler; // 
		protected var _mouseOutHandler:IMouseHandler; //

		protected var _mouseDownHandler:IMouseHandler; // 
		protected var _mouseUpHandler:IMouseHandler; // 

		protected var _mouseMoveHandler:IMouseHandler; //

		/**
		 *
		 */
		public function Tile(colSpan:Number = 1, rowSpan:Number = 1, height:Number = 0, walkable:Boolean = false) {
			container.mouseChildren = false;

			_skins = [];
			_walkable = walkable;
			_colSpan = colSpan;
			_rowSpan = rowSpan;
			this.movable = true;
			this.selectable = true;
			
			this.height = height;

			// by default
			this.moveBehavior 		= new BasicMove();
			//this.mouseClickHandler 	= new ToggleSelection();

			super.addEventListener(MouseEvent.CLICK, 		this.onMouseClick);
			super.addEventListener(MouseEvent.MOUSE_OVER, 	this.onMouseOver);
			super.addEventListener(MouseEvent.MOUSE_OUT, 	this.onMouseOut);
			super.addEventListener(MouseEvent.MOUSE_DOWN, 	this.onMouseDown);
			super.addEventListener(MouseEvent.MOUSE_UP, 	this.onMouseUp);
			super.addEventListener(MouseEvent.MOUSE_MOVE, 	this.onMouseMove);
		}

		/**
		 * This is called when the world is set.
		 *
		 * @param	width   Column span
		 * @param	length  Row span
		 * @param	height
		 */
		override public function setSize(width:Number, length:Number, height:Number):void {
			Logger.log("Tile.setSize("+arguments+")");
			super.setSize(_cellSize * _colSpan, _cellSize * _rowSpan, height);
		}


		/**
		 * Put the tile on a specific location regardless to its movement behavior.
		 *
		 * @param	col
		 * @param	row
		 * @param	z
		 */
		public function spawnAt(col:Number, row:Number, z:Number = 0):void {
			//temporarily change the movement to BasicMove
			var b:TileMovement = _moveBehavior;
			moveBehavior = new BasicMove();
			moveBehavior.execute([col, row, z]);

			//put back the original movement behavior
			moveBehavior = b;
		}
		
		/**
		 * Move the tile to a specific location.
		 * 
		 * @param	x  column
		 * @param	y  row
		 * @param	z
		 */
		override public function moveTo(x:Number, y:Number, z:Number):void {
			Logger.log("Tile.moveTo(" + arguments + ")");
			
			if( this.movable ) {
				_moveBehavior.execute([x, y, z]);
			}
		}

		override public function invalidatePosition():void {
			super.invalidatePosition();

			if (Math.floor(x / _cellSize) == _column && Math.floor(y / _cellSize) == _row) {
				return;
			}

			_column = Math.floor(x / _cellSize);
			_row = Math.floor(y / _cellSize);

		}

//{ region  ----------- SKINNING

		/**
		 * Add skin
		 *
		 * @param	skin		the skin asset
		 * @param	descriptor	object containing extra properties :
		 *                         - x,y (skin coordinates in the tile's container sprite) ,
		 *                         - scales
		 */
		public function addSkin(skin:DisplayObject, descriptor:SkinDescriptor = null):void {
			if(descriptor == null) {
				descriptor = new SkinDescriptor(0, 0, 1, 1);
			}
			skin.x = descriptor.x;
			skin.y = descriptor.y;
			skin.scaleX = descriptor.scaleX;
			skin.scaleY = descriptor.scaleY;
			
			skin.cacheAsBitmap = true;
			sprites.push(skin);
			_skins.push(skin);

			invalidateSprites();
		}

		public function removeSkin(skin:DisplayObject):void {
			try {
				//ArrayUtil.removeValueFromArray(sprites, skin);
				//ArrayUtil.removeValueFromArray(_skins, skin);
				//invalidateSprites();
			} catch (e:Error) {
				// 
			}
		}

		/**
		 * Add an animated sprite sheet.
		 * @param	sheet		the sheet
		 * @param	descriptor	extra properties.
		 * @see 	addSkin()
		 */
		public function addAnimationSheet(sheet:SpriteSheet, descriptor:SkinDescriptor = null):void {
			Logger.log("Tile.addAnimationSheet("+arguments+")");
			sheet.idle();
			sheet.setDirection(_faceDirection);
			addSkin(sheet, descriptor);
		}

		public function removeAnimationSheet(sheet:SpriteSheet):void {
			removeSkin(sheet);
		}

		public function clearSkins():void {
			sprites = [];
			_skins = [];

			invalidateSprites();
		}

		/**
		 * skin
		 */
		protected function createBaseSprite(color:uint = 0):Sprite {
			var s:Sprite = new Sprite();
			s.graphics.beginFill(color, .5);
			IsoDrawingUtil.drawIsoRectangle(s.graphics, new Pt(0, 0, 0), width, length)
			s.graphics.endFill();
			return s;
		}

		/**
		 * highlight the tile
		 *
		 * @parameter enable	true indicate highlight the skin of tile. otherwise resume.
		 * @parameter color		the color of hight. meanless when enable is false.
		 */
		public function highLight(enable:Boolean, color:int = 0xFFFF00):void {
			for (var i:int = 0; i < super.sprites.length; i++) {
				// highlist the skin except the base of tile.
				if (super.sprites[i] != this._baseSprite) {
					super.sprites[i].filters = enable ? [new GlowFilter(color, 1, 4, 4, 4, 1, true)] : null;
				}
			}
		}
		
		protected function onMouseClick(e:ProxyEvent):void {
			super.dispatchEvent(new TileEvent(TileEvent.CLICK));
		}
		protected function onMouseOver(e:ProxyEvent):void {
			this.highLight(true);
			super.dispatchEvent(new TileEvent(TileEvent.OVER));
		}
		protected function onMouseOut(e:ProxyEvent):void {
			this.highLight(false);
			super.dispatchEvent(new TileEvent(TileEvent.OUT));
		}
		protected function onMouseDown(e:ProxyEvent):void {
			Logger.log("Tile.onMouseDown("+e+")");
			//this.selected = true;
			super.dispatchEvent(new TileEvent(TileEvent.DOWN));
		}
		protected function onMouseUp(e:ProxyEvent):void {
			Logger.log("Tile.onMouseUp("+e+")");
			//this.selected = false;
			super.dispatchEvent(new TileEvent(TileEvent.UP));
		}
		protected function onMouseMove(e:ProxyEvent):void {
			super.dispatchEvent(new TileEvent(TileEvent.MOVE));
		}		
		
//} endregion SKINNING

		/**********************************************
		 * State
		 **********************************************/
		public function get isIdle():Boolean {
			return _isIdle;
		}

		public function set isIdle(value:Boolean):void {

			_isIdle = value;

			for (var i:int = 0, len:int = _skins.length; i < len; i++) {
				if ((_skins[i] is SpriteSheet) == false) {
					continue;
				}
				var tsheet:SpriteSheet = SpriteSheet(_skins[i]);

				if (_isIdle) {
					tsheet.idle();
				} else {
					tsheet.action();
				}
			}

		}

		public function get faceDirection():String {
			return _faceDirection;
		}

		public function set faceDirection(value:String):void {
			Logger.log("Tile.faceDirection("+arguments+")");
			if (value == _faceDirection) {
				return;
			}

			_faceDirection = value;

			if (_skins.length > 0) {
				for (var i:int = 0, len:int = _skins.length; i < len; i++) {
					if ((_skins[i] is SpriteSheet) == false) {
						continue;
					}
					var tsheet:SpriteSheet = _skins[i];
					tsheet.setDirection(_faceDirection);
				}
			}
		}

		public function get column():Number {
			return _column;
		}

		public function get row():Number {
			return _row;
		}

		public function get grid():IsoGrid {
			return _grid;
		}

		public function get world():World {
			return _world;
		}

		public function set world(world:World):void {
			_world = world;
			_grid = world.grid;
			_cellSize = _grid.cellSize;
			setSize(_colSpan, _rowSpan, height);
		}

		//{ region  ----------- GETTERS/SETTERS

		public function get selected():Boolean {
			return _selected;
		}
		public function set selected(value:Boolean):void {
			Logger.log("Tile.selected("+value+"): "+name + " selected " + _selected+ ",  _skins="+_skins);
			if(!this.selectable)	return;
				
			_selected = value;
			if (_selected) {
				Logger.log("Tile.selected    SELECTED");
				_baseSprite = this.createBaseSprite(0xFF0000);
				if (_skins.length < 1) {
					sprites[0] = _baseSprite;
				} else {
					sprites.unshift(_baseSprite);
				}
				dispatchEvent(new TileEvent(TileEvent.SELECTED));
			} else {
				Logger.log("Tile.selected    DE_SELECTED"+_skins);
				// 
				_baseSprite = this.createBaseSprite(0x000000);
				if (_skins.length < 1) {
					sprites[0] = _baseSprite;
				} else {
					sprites.shift();
				}
				dispatchEvent(new TileEvent(TileEvent.DESELECTED));
			}
			invalidateSprites();
		}
		
		

		public function get moveBehavior():TileMovement {
			return _moveBehavior;
		}

		public function set moveBehavior(value:TileMovement):void {
			this._moveBehavior = value;
			if(value)
				this._moveBehavior.tile = this;
		}

		public function get walkable():Boolean {
			return _walkable;
		}

		public function set walkable(value:Boolean):void {
			_walkable = value;
		}
		
		
		public function get movable():Boolean {
			return this._movable;
		}
		public function set movable(value:Boolean):void {
			this._movable = value;
		}
		
		public function set isMouseDown(value:Boolean):void {
			this._mouseDown = value;
		}
		public function get isMouseDown():Boolean{
			return this._mouseDown;
		}
		
		
		override public function get name():String {
			return _name;
		}

		override public function set name(value:String):void {
			_name = value;
		}

		public function get uniqueName():String {
			return _name + "_" + positionAsString;
		}

		/**********************************************
		 * GRID-RELATED
		 **********************************************/

		public function get positionAsString():String {
			return String(column) + "_" + String(row) + "_" + String(z);
		}

		/**
		 *
		 */
		public function get colSpan():Number {
			return _colSpan;
		}

		public function get rowSpan():Number {
			return _rowSpan;
		}

		/**
		 *
		 */
		public function get mouseClickHandler():IMouseHandler {
			return _mouseClickHandler;
		}

		public function set mouseClickHandler(value:IMouseHandler):void {
			_mouseClickHandler = value;
			if (value)
				_mouseClickHandler.tile = this;
		}

		/**
		 *
		 */
		public function get mouseOverHandler():IMouseHandler {
			return _mouseOverHandler;
		}

		public function set mouseOverHandler(value:IMouseHandler):void {
			_mouseOverHandler = value;
			if (value)
				_mouseOverHandler.tile = this;
		}

		/**
		 *
		 */
		public function get mouseOutHandler():IMouseHandler {
			return _mouseOutHandler;
		}

		public function set mouseOutHandler(value:IMouseHandler):void {
			_mouseOutHandler = value;
			if (value)
				_mouseOutHandler.tile = this;
		}

		/**
		 *
		 */
		public function get mouseDownHandler():IMouseHandler {
			return _mouseDownHandler;
		}

		public function set mouseDownHandler(value:IMouseHandler):void {
			_mouseDownHandler = value;
			if (value)
				_mouseDownHandler.tile = this;
		}

		/**
		 *
		 */
		public function get mouseUpHandler():IMouseHandler {
			return _mouseUpHandler;
		}

		public function set mouseUpHandler(value:IMouseHandler):void {
			_mouseUpHandler = value;
			if (value)
				_mouseUpHandler.tile = this;
		}

		/**
		 *
		 */
		public function get mouseMoveHandler():IMouseHandler {
			return _mouseMoveHandler;
		}

		public function set mouseMoveHandler(value:IMouseHandler):void {
			_mouseMoveHandler = value;
			if (value)
				_mouseMoveHandler.tile = this;
		}
	}

}